import {
  ChangeDetectorRef,
  Component,
  EventEmitter,
  Input,
  OnDestroy,
  OnInit,
  Output,
} from '@angular/core';
import {
  ErrorResponse,
  VulnerabilityAsset,
  VulnerabilityProfile,
} from '@common/types';
import {
  ColDef,
  GridApi,
  GridOptions,
  GridReadyEvent,
  RowNode,
  ValueFormatterParams,
} from 'ag-grid-community';
import { TranslateService } from '@ngx-translate/core';
import { VulnerabilityItemsTableCsvCellComponent } from './vulnerability-items-table-csv-cell/vulnerability-items-table-csv-cell.component';
import { VulnerabilityItemsTableImpactCellComponent } from './vulnerability-items-table-impact-cell/vulnerability-items-table-impact-cell.component';
import { VulnerabilityItemsTableScoreCellComponent } from './vulnerability-items-table-score-cell/vulnerability-items-table-score-cell.component';
import { VulnerabilityItemsTableSevertiyCellComponent } from './vulnerability-items-table-severtiy-cell/vulnerability-items-table-severtiy-cell.component';
import { VulnerabilitiesService } from '../../vulnerabilities.service';
import { VulnerabilitiesFilterService } from '../../vulnerabilities.filter.service';
import { MatDialog, MatDialogRef } from '@angular/material/dialog';
import { VulnerabilityItemsTableFilterComponent } from './vulnerability-items-table-filter/vulnerability-items-table-filter.component';
import { DatePipe } from '@angular/common';
import { UtilsService } from '@common/utils/app.utils';
import { NotificationService } from '@services/notification.service';

@Component({
  selector: 'app-vulnerability-items-table',
  templateUrl: './vulnerability-items-table.component.html',
  styleUrls: ['./vulnerability-items-table.component.scss'],
})
export class VulnerabilityItemsTableComponent implements OnInit, OnDestroy {
  @Input() rowData!: VulnerabilityAsset[];
  @Output() toggleChartView = new EventEmitter();
  @Input() domains!: string[];
  selectedVulnerability;
  matchTypes = this.vulnerabilitiesFilterService.matchTypes;
  dateTypes = this.vulnerabilitiesFilterService.dateTypes;
  get activeScore() {
    return this.vulnerabilitiesFilterService.selectedScore === 'V2'
      ? this.translate.instant('scan.gridHeader.SCORE_V2')
      : this.translate.instant('scan.gridHeader.SCORE_V3');
  }
  gridOptions!: GridOptions;
  gridApi!: GridApi;
  filteredCount = 0;
  filterDialog!: MatDialogRef<any>;
  advFilter: any = this.vulnerabilitiesFilterService.advFilter;
  filterOpen = false;
  filtered$ = this.vulnerabilitiesFilterService.filtered$;
  columnDefs: ColDef[] = [
    {
      field: 'name',
      sortable: true,
      resizable: true,
      headerValueGetter: () => this.translate.instant('scan.gridHeader.NAME'),
    },
    {
      field: 'severity',
      sortable: true,
      resizable: true,
      cellRenderer: 'severityCellRenderer',
      headerValueGetter: () =>
        this.translate.instant('scan.gridHeader.SEVERITY'),
    },
    {
      field: 'score',
      hide: true,
      sortable: true,
      resizable: true,
      cellRenderer: 'scoreCellRenderer',
      cellRendererParams: {
        type: 'V2',
      },
      headerValueGetter: () =>
        this.translate.instant('scan.gridHeader.SCORE_V2'),
    },
    {
      field: 'score_v3',
      sortable: true,
      resizable: true,
      cellRenderer: 'scoreCellRenderer',
      cellRendererParams: {
        type: 'V3',
      },
      headerValueGetter: () =>
        this.translate.instant('scan.gridHeader.SCORE_V3'),
    },
    {
      field: 'published_timestamp',
      sortable: true,
      resizable: true,
      valueFormatter: this.dateFormatter,
      headerValueGetter: () =>
        this.translate.instant('scan.gridHeader.PUBLISHED_TIME'),
    },
    {
      resizable: true,
      cellRenderer: 'impactCellRenderer',
      sortable: true,
      comparator: this.impactComparator,
      headerValueGetter: () => this.translate.instant('scan.gridHeader.VICTIM'),
    },
    {
      resizable: true,
      cellRenderer: 'csvCellRenderer',
      headerValueGetter: () => 'CSV',
    },
  ];

  constructor(
    private translate: TranslateService,
    private cd: ChangeDetectorRef,
    private vulnerabilitiesService: VulnerabilitiesService,
    private vulnerabilitiesFilterService: VulnerabilitiesFilterService,
    private notificationService: NotificationService,
    public dialog: MatDialog,
    private datePipe: DatePipe,
    private utils: UtilsService
  ) {}

  ngOnInit(): void {
    this.gridOptions = {
      rowData: this.rowData,
      columnDefs: this.columnDefs,
      suppressDragLeaveHidesColumns: true,
      rowSelection: 'single',
      onGridReady: event => this.onGridReady(event),
      onSelectionChanged: event => this.onSelectionChanged(event),
      components: {
        impactCellRenderer: VulnerabilityItemsTableImpactCellComponent,
        csvCellRenderer: VulnerabilityItemsTableCsvCellComponent,
        scoreCellRenderer: VulnerabilityItemsTableScoreCellComponent,
        severityCellRenderer: VulnerabilityItemsTableSevertiyCellComponent,
      },
      doesExternalFilterPass: this.doesExternalFilterPass.bind(this),
      isExternalFilterPresent: this.isExternalFilterPresent.bind(this),
      overlayNoRowsTemplate: this.translate.instant('general.NO_ROWS'),
    };
  }

  acceptVul() {
    const payload: VulnerabilityProfile = {
      entries: [
        {
          name: this.selectedVulnerability.name,
          days: 0,
          comment: `Accepted by ${'REPLACE WITH USER'} at ${this.datePipe.transform(
            new Date(),
            'MMM dd, y HH:mm:ss'
          )} from Vulnerabilities page`,
          images: [],
          domains: [],
        },
      ],
      name: 'default',
    };
    this.vulnerabilitiesService.acceptVulnerability(payload).subscribe({
      complete: () => {
        this.notificationService.open(
          this.translate.instant('cveProfile.msg.ADD_OK')
        );
      },
      error: ({ error }: { error: ErrorResponse }) => {
        this.notificationService.open(
          this.utils.getAlertifyMsg(
            error,
            this.translate.instant('cveProfile.msg.ADD_NG'),
            false
          )
        );
      },
    });
  }

  changeScoreView(val: string) {
    this.vulnerabilitiesFilterService.selectedScore = val;
    if (val === 'V2') {
      this.gridOptions.columnApi?.setColumnVisible('score_v3', false);
      this.gridOptions.columnApi?.setColumnVisible('score', true);
    } else {
      this.gridOptions.columnApi?.setColumnVisible('score', false);
      this.gridOptions.columnApi?.setColumnVisible('score_v3', true);
    }
    this.gridApi.sizeColumnsToFit();
    this.cd.markForCheck();
  }

  doesExternalFilterPass(node: RowNode) {
    if (!this.vulnerabilitiesFilterService.isAdvFilterOn()) return true;
    else {
      let result = true;
      if (this.advFilter.dt) {
        if (
          this.advFilter.publishedType &&
          this.advFilter.publishedType.id === 'before'
        )
          result =
            result &&
            node.data.published_timestamp <=
              Math.floor(new Date(this.advFilter.dt).getTime() / 1000);
        else
          result =
            result &&
            node.data.published_timestamp >=
              Math.floor(new Date(this.advFilter.dt).getTime() / 1000);
      }
      if (this.advFilter.severityType !== 'all') {
        result = result && node.data.severity === this.advFilter.severityType;
      }
      if (this.advFilter.packageType === 'withFix') {
        let packagePairs = Object.entries(node.data.packages);
        if (packagePairs.length) {
          const hasFix = packagePairs.find(
            ([key, val]: any) =>
              val.length && val.find(lib => !!lib.fixed_version)
          );
          result = result && !!hasFix;
        } else return false;
      }
      if (this.advFilter.packageType === 'withoutFix') {
        let packagePairs = Object.entries(node.data.packages);
        if (packagePairs.length) {
          const hasFix = packagePairs.find(
            ([key, val]: any) =>
              val.length && val.find(lib => !!lib.fixed_version)
          );
          if (hasFix) return false;
        }
      }
      if (this.advFilter.containerName) {
        if (node.data.workloads.length) {
          result = this.checkEntity(
            this.advFilter.matchTypes['Container'].id,
            node.data.workloads,
            this.advFilter.containerName,
            result
          );
        } else return false;
      }
      if (this.advFilter.nodeName) {
        if (node.data.nodes.length) {
          result = this.checkEntity(
            this.advFilter.matchTypes['Node'].id,
            node.data.nodes,
            this.advFilter.nodeName,
            result
          );
        } else return false;
      }
      if (this.advFilter.imageName) {
        if (node.data.images.length) {
          result = this.checkEntity(
            this.advFilter.matchTypes['Image'].id,
            node.data.images,
            this.advFilter.imageName,
            result
          );
        } else return false;
      }
      if (this.advFilter.selectedDomains.length) {
        result = this.checkEntity(
          this.advFilter.matchType4Ns.id,
          node.data.domains,
          this.advFilter.selectedDomains
            .map(selectedDomain => selectedDomain.name)
            .join(','),
          result
        );
      }
      if (this.advFilter.serviceName) {
        if (node.data.services?.length) {
          result = this.checkEntity(
            this.advFilter.matchTypes['Service'].id,
            node.data.services,
            this.advFilter.serviceName,
            result
          );
        } else return false;
      }
      if (this.advFilter.sliderV2.minValue) {
        result = result && node.data.score > this.advFilter.sliderV2.minValue;
      }
      if (this.advFilter.sliderV2.maxValue < 10) {
        result = result && node.data.score < this.advFilter.sliderV2.maxValue;
      }
      if (this.advFilter.sliderV3.minValue) {
        result =
          result && node.data.score_v3 > this.advFilter.sliderV3.minValue;
      }
      if (this.advFilter.sliderV3.maxValue < 10) {
        result =
          result && node.data.score_v3 < this.advFilter.sliderV3.maxValue;
      }
      return result;
    }
  }

  isExternalFilterPresent(): boolean {
    return this.vulnerabilitiesFilterService.isAdvFilterOn();
  }

  onGridReady(params: GridReadyEvent): void {
    this.gridApi = params.api;
    this.changeScoreView(this.vulnerabilitiesFilterService.selectedScore);
    this.gridApi.sizeColumnsToFit();
    this.gridApi.forEachNode(node =>
      node.rowIndex ? 0 : node.setSelected(true)
    );
    this.cd.markForCheck();
  }

  checkEntity(matchType, entities, pattern, result) {
    const patterns = pattern.split(',').map(item => item.trim());
    const theEntity = entities.find(entity => {
      if (entity && entity.display_name) {
        if (matchType === 'equal')
          return patterns.some(item => item === entity.display_name);
        else return new RegExp(patterns.join('|')).test(entity.display_name);
      } else {
        if (matchType === 'equal')
          return patterns.some(item => item === entity);
        else return new RegExp(patterns.join('|')).test(entity);
      }
    });
    result = result && !!theEntity;
    return result;
  }

  // runWorkers() {
  //   const filteredCis: any = [];
  //   this.gridApi.forEachNodeAfterFilterAndSort(node => {
  //     filteredCis.push(JSON.parse(JSON.stringify(node.data)));
  //   });
  //   this.vulnerabilitiesFilterService.filteredCis = filteredCis;
  //   this.vulnerabilitiesService.runWorkers();
  // }

  filterCountChanged(results: number) {
    this.filteredCount = results;
    this.vulnerabilitiesFilterService.filtered =
      this.filteredCount !== this.rowData.length;
    // this.runWorkers();
  }

  onResize(): void {
    this.gridApi.sizeColumnsToFit();
  }

  onSelectionChanged(params: GridReadyEvent): void {
    this.toggleChartView.emit(false);
    this.selectedVulnerability = params.api.getSelectedNodes()[0].data;
    this.vulnerabilitiesService.selectVulnerability(
      params.api.getSelectedNodes()[0].data
    );
  }

  onToggleChartView() {
    this.toggleChartView.emit();
  }

  openAdvancedFilter(): void {
    if (!this.filterOpen) {
      this.filterOpen = true;
      this.filterDialog = this.dialog.open(
        VulnerabilityItemsTableFilterComponent,
        {
          width: '675px',
          data: { filter: this.advFilter, domains: this.domains },
          hasBackdrop: false,
          position: { right: '25px', top: '130px' },
        }
      );

      const convertMatchType = id => {
        return id === 'equal' ? this.matchTypes[0] : this.matchTypes[1];
      };

      const convertDateType = id => {
        return id === 'before' ? this.dateTypes[0] : this.dateTypes[1];
      };

      this.filterDialog.afterClosed().subscribe(filter => {
        if (filter && filter.reset) {
          this.vulnerabilitiesFilterService.resetFilter();
          this.setAdvancedFilter();
        } else if (filter) {
          filter.matchType4Ns = convertMatchType(filter.matchType4Ns);
          filter.matchTypes.Service = convertMatchType(
            filter.matchTypes.Service
          );
          filter.matchTypes.Container = convertMatchType(
            filter.matchTypes.Container
          );
          filter.matchTypes.Node = convertMatchType(filter.matchTypes.Node);
          filter.matchTypes.Image = convertMatchType(filter.matchTypes.Image);
          filter.sliderV2 = {
            minValue: filter.sliderV2[0],
            maxValue: filter.sliderV2[1],
          };
          filter.sliderV3 = {
            minValue: filter.sliderV3[0],
            maxValue: filter.sliderV3[1],
          };
          filter.publishedType = convertDateType(filter.publishedType);
          this.setAdvancedFilter(filter);
        }
        this.filterOpen = false;
      });
    }
  }

  setAdvancedFilter(filter?: any) {
    if (filter) {
      this.vulnerabilitiesFilterService.advFilter = filter;
    }
    this.advFilter = this.vulnerabilitiesFilterService.advFilter;
    this.gridApi.onFilterChanged();
    this.filteredCount =
      this.gridApi.getModel()['rootNode'].childrenAfterFilter.length;
    // this.runWorkers();
  }

  dateFormatter(params: ValueFormatterParams): string {
    const date = new Date(params.data.published_timestamp * 1000);
    const dateString = date.toDateString().split(' ').slice(1);
    dateString[1] = dateString[1] + ',';
    return dateString.join(' ');
  }

  ngOnDestroy() {
    if (this.filterOpen) {
      this.filterDialog.close();
    }
  }

  private impactComparator(value1, value2, node1, node2) {
    const cve1 = node1.data;
    const cve2 = node2.data;
    if (cve1.platforms.length === cve2.platforms.length) {
      if (cve1.images.length === cve2.images.length) {
        if (cve1.nodes.length === cve2.nodes.length) {
          return cve1.workloads.length - cve2.workloads.length;
        } else return cve1.nodes.length - cve2.nodes.length;
      } else return cve1.images.length - cve2.images.length;
    } else {
      return cve1.platforms.length - cve2.platforms.length;
    }
  }
}
